package yawning.mew.bear;

import yawning.mew.ProcEffect.Trigger;
import yawning.mew.character.CharacterModel.AttackResult;
import yawning.mew.sim.SimulationAction;
import yawning.mew.sim.SimulationEventAction;

public class LacerateAction extends SimulationEventAction {
	BearThreatSimulator mState;

	private double mAttackPower;
	private double mPCrit;
	private double mEnrageMult;
	private double mToTTMult;
	private int mStacks = 0;

	LacerateAction(BearThreatSimulator state) {
		super(state.mSimEngine);

		mState = state;
	}

	@Override
	public double perform() {
		int cost = mState.mModel.lacerateCost;

		if (!mState.hasRage(cost) && !mState.mOoCEv.isUp()) return SimulationAction.ACTION_FAIL;

		AttackResult result = mState.mModel.getFrontalYellowResult(mState.mModel.pBonusLacerateCrit);

		if (result == AttackResult.MISS) {
			mNrLacerateMiss++;

			if (mState.mIsLogging) mState.mLog.log("Lacerate: MISS\n");
		} else if (result == AttackResult.DODGE) {
			mNrLacerateDodge++;

			if (mState.mIsLogging) mState.mLog.log("Lacerate: DODGE\n");
		} else if (result == AttackResult.PARRY) {
			mNrLacerateParry++;

			if (mState.mIsLogging) mState.mLog.log("Lacerate: PARRY\n");
		} else {
			double damage = mState.mModel.mLevelDep.getLacerateDmg() + mState.mModel.getAttackPower() * mState.mModel.mLevelDep.getLacerateDmgCoeff();

			if (result == AttackResult.CRIT) {
				mNrLacerateCrit++;
				damage *= mState.mModel.multCrit;
			} else {
				mNrLacerateHit++;
			}

			// Per Tangedyn, 4.1 live, this is affected by armor.
			mStacks = Math.min(3, mStacks + 1);
			damage *= mState.getBleedDamageMult();
			damage *= mState.mToTTEv.getMult();
			damage *= (1 - mState.mActiveTargetData.getArmorDR(mState.mModel.mToon.level));
			damage *= mState.mEnrageAction.getMult();

			// Snapshot the stats.
			mAttackPower = mState.mModel.getAttackPower();
			mPCrit = Math.min(1, mState.mModel.getMeleeCritChance() + mState.mModel.pBonusLacerateCrit);
			mEnrageMult = mState.mEnrageAction.getMult();
			mToTTMult = mState.mToTTEv.getMult();

			if (!isUp())
				schedule(mState.mModel.lacerateDebuffDuration, mState.mModel.lacerateTickInterval);
			else
				extendExpiry(mState.mModel.lacerateDebuffDuration + getNextTickTime() - getExpiryTime());

			mLacerateDamage += Math.round(damage);

			if (mState.mIsLogging) mState.mLog.log("Lacerate: %s Damage: %d Stacks: %d\n", result, Math.round(damage), mStacks);

			if (result == AttackResult.CRIT) {
				mState.mSavageDefenseEv.onTrigger();
				mState.mLotPEv.onTrigger();
				mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.YELLOWCRIT);
			} else {
				mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.YELLOW);
			}
		}

		mState.onRageLoss(cost);

		if (result == AttackResult.CRIT) mState.onPrimalFuryTrigger();

		return 1.5;
	}

	@Override
	public void onTick() {
		double damage = mState.mModel.mLevelDep.getLacerateDoTDmg() + mAttackPower * mState.mModel.mLevelDep.getLacerateDoTCoeff();
		AttackResult result = mState.mModel.getDotResult(mPCrit);

		if (result == AttackResult.CRIT) {
			mNrLacerateDoTCrit++;
			damage *= mState.mModel.multCrit;
		} else {
			mNrLacerateDoTHit++;
		}

		damage *= mState.getBleedDamageMult();
		damage *= mEnrageMult;
		damage *= mToTTMult;
		damage *= mStacks;
		damage *= mState.mModel.tier_11_2pc_lacerateDoTMult;

		mLacerateDoTDamage += Math.round(damage);

		if (mState.mIsLogging) mState.mLog.log("Lacerate Tick: %s Damage: %d Stacks: %d\n", result, Math.round(damage), mStacks);

		if (result == AttackResult.CRIT) mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.DOTCRIT); else mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.DOT);


		mState.mBerserkProcEv.onTrigger();
	}

	@Override
	public void onExpire() {
		if (mState.mIsLogging) mState.mLog.log("Lacerate Expiration\n");

		mStacks = 0;
	}

	int getStacks() {
		return mStacks;
	}

	void onPulverize() {
		cancel();
	}

	long mLacerateDamage = 0;
	int mNrLacerateMiss = 0;
	int mNrLacerateDodge = 0;
	int mNrLacerateParry = 0;
	int mNrLacerateCrit = 0;
	int mNrLacerateHit = 0;
	long mLacerateDoTDamage = 0;
	int mNrLacerateDoTCrit = 0;
	int mNrLacerateDoTHit = 0;
}
